package ejemplo.sistemas;

import java.util.ArrayList;

import ejemplo.componentes.interfaces.IColisionable;
import ejemplo.util.UtilidadColision;
import ejemplo.util.Vector2D;
import es.componentes.Componente;
import es.gestores.GestorComponentes;
import es.gestores.GestorComponentesListener;
import es.subsistemas.Subsistema;
/**
 * Subsistema de colisiones.
 * @author Miguel Vicente
 */
public class SubsistemaColisiones implements Subsistema, GestorComponentesListener {
	
	private ArrayList<Componente> componentesColisionablesNoMapa;
	private Mapa mapa;
	private String idLosa;
	
	/**
	 * Constructor.
	 * @param componentes ArrayList de componentes a procesar en busca de colisiones.
	 * @param mapa Mapa
	 */
	public SubsistemaColisiones(ArrayList<Componente> componentes, String idLosa, int nFilasMapa, int nColumnasMapa, int anchoLosaMapa, int altoLosaMapa, GestorComponentes gestorComponentes) {
		this.idLosa = idLosa;
		this.componentesColisionablesNoMapa = new ArrayList<Componente>();
		this.mapa = new Mapa(nFilasMapa, nColumnasMapa, anchoLosaMapa, altoLosaMapa);
		cargarEstructurasDatos(componentes);
		gestorComponentes.addGestorComponentesListener(this);
	}

	@Override
	public String getTipo() {
		return IColisionable.ID_INTERFAZ;
	}
	
	@Override
	public void procesar(double delta) {
		// Comprobamos las colisiones entre los componentes que no forman parte del mapa.
		for (int i = 0; i < componentesColisionablesNoMapa.size(); i++) {
			for (int j = 0; j < componentesColisionablesNoMapa.size(); j++) {
				if (i != j) {
					procesarComponente(componentesColisionablesNoMapa.get(i), componentesColisionablesNoMapa.get(j));
				}
			}
		}
		// Comprobamos las colisiones con el mapa
		// ...
	}
	
	@Override
	public void componenteAgregado(Componente componente) {
		if (componente.getTipo() == IColisionable.ID_INTERFAZ) {
			if (componente.getEntidad().getTipo() == idLosa) {
				IColisionable c = (IColisionable) componente;
				int x = mapa.getLosaPixelX(c.getBounds().x);
				int y = mapa.getLosaPixelY(c.getBounds().y);
				mapa.componentesColisionLosas[x][y] = c;
			} else {
				componentesColisionablesNoMapa.add(componente);
			}
		}
	}

	@Override
	public void componenteEliminado(Componente componente) {
		if (componente.getTipo() == IColisionable.ID_INTERFAZ) {
			if (componente.getEntidad().getTipo() == idLosa) {
				IColisionable c = (IColisionable) componente;
				int x = mapa.getLosaPixelX(c.getBounds().x);
				int y = mapa.getLosaPixelY(c.getBounds().y);
				mapa.componentesColisionLosas[x][y] = null;
			} else {
				for (int i = 0; i < componentesColisionablesNoMapa.size(); i++) {
					if (componentesColisionablesNoMapa.get(i).getId() == componente.getId()) {
						componentesColisionablesNoMapa.remove(i);
					}
				}
			}
		}
	}
	/**
	 * Procesa un componente c1, comprobando si colisiona con un Componente c2.<br>
	 * @param c1 Componente
	 * @param c2 Componente
	 */
	public void procesarComponente(Componente c1, Componente c2) {
		IColisionable colisionable1 = (IColisionable) c1;
		IColisionable colisionable2 = (IColisionable) c2;
		Vector2D penetracion = UtilidadColision.getPenetracion(colisionable1, colisionable2);
		// Gestionar la colision
		// ...
	}
	/**
	 * Castea un {@code ArrayList<Componente>} a {@code ArrayList<IColisionable>}
	 * @param componentes {@code ArrayList<Componente>}
	 * @return {@code ArrayList<IColisionable>}
	 */
	private ArrayList<IColisionable> castComponentesIColisionable(ArrayList<Componente> componentes) {
		ArrayList<IColisionable> iColisionables = new ArrayList<IColisionable>();
		for (Componente componente : componentes) {
			iColisionables.add((IColisionable) componente);
		}
		return iColisionables;
	}
	
	private void cargarEstructurasDatos(ArrayList<Componente> componentes) {
		for (Componente c : componentes) {
			if (c.getEntidad().getTipo() == idLosa) {
				IColisionable comp = (IColisionable) c;
				int x = mapa.getLosaPixelX(comp.getBounds().x);
				int y = mapa.getLosaPixelY(comp.getBounds().y);
				mapa.componentesColisionLosas[x][y] = comp;
			} else {
				componentesColisionablesNoMapa.add(c);
			}
		}
	}
	/**
	 * Clase privada que agrupa los componentes de colision de las losas del mapa.
	 * @author Miguel Vicente
	 */
	private class Mapa {
		
		private IColisionable[][] componentesColisionLosas;
		private int anchoLosa;
		private int altoLosa;
		
		public Mapa(int nFilas, int nColumnas, int anchoLosa, int altoLosa) {
			this.componentesColisionLosas = new IColisionable[nFilas][nColumnas];
			this.anchoLosa = anchoLosa;
			this.altoLosa = altoLosa;
		}
		/**
		 * Devuelve la losa, en el eje X, en la que se encuentra el pixel indicado.
		 * @param pixel
		 * @return
		 */
		public int getLosaPixelX(double pixel) {
			return (int) (pixel / anchoLosa);
		}
		/**
		 * Devuelve la losa, en el eje Y, en la que se encuentra el pixel indicado.
		 * @param pixel
		 * @return
		 */
		public int getLosaPixelY(double pixel) {
			return (int) (pixel / altoLosa);
		}
		
	}

}
